home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
DJLGR106.ARJ
/
REGION.CC
< prev
next >
Wrap
C/C++ Source or Header
|
1992-03-29
|
12KB
|
566 lines
/* This is file REGION.CC */
/*
** Copyright (C) 1991 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954
**
** This file is distributed under the terms listed in the document
** "copying.dj", available from DJ Delorie at the address above.
** A copy of "copying.dj" should accompany this file; if not, a copy
** should be available from where this file was obtained. This file
** may not be distributed without a verbatim copy of "copying.dj".
**
** This file is distributed WITHOUT ANY WARRANTY; without even the implied
** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/* History:251,33 */
#include <std.h>
#include <sys/registers.h>
#include "graphics.h"
static GrRegion *screen_region = 0;
extern int _GrCurMode;
static void SetModeHook()
{
screen_region->width = GrSizeX();
screen_region->height = GrSizeY();
screen_region->row_scale = GrSizeX();
screen_region->data = (unsigned char *)0xd0000000;
screen_region->rdata = (unsigned char *)0xd0100000;
screen_region->wdata = (unsigned char *)0xd0200000;
}
extern int _GrSetModeHook;
GrRegion *GrScreenRegion()
{
if (_GrCurMode < GR_320_200_graphics)
GrSetMode(GR_default_graphics);
if (!screen_region)
screen_region = new GrRegion();
_GrSetModeHook = (int)SetModeHook;
SetModeHook();
return screen_region;
}
GrRegion::GrRegion()
{
flags = width = height = row_scale = 0;
rdata = wdata = data = 0;
parent = 0;
rel_x = rel_y = abs_x = abs_y = 0;
color = GrWhite();
}
GrRegion::GrRegion(int w, int h)
{
flags = 1;
rdata = wdata = data = (unsigned char *)malloc(w*h);
bzero(data,w*h);
width = w;
height = h;
row_scale = w;
color = GrWhite();
parent = 0;
rel_x = rel_y = abs_x = abs_y = 0;
}
GrRegion::~GrRegion()
{
if (flags && data)
free(data);
}
GrRegion *GrRegion::SubRegion(int x, int y, int w, int h)
{
if (!data) return 0;
GrRegion *tmp = new GrRegion();
if (!tmp) return 0;
if ((x < 0) || (y < 0))
return tmp;
if ((x >= width) || (y >= height))
return tmp;
if (x+w > width)
w = width - x;
if (y+h > height)
h = height - y;
tmp->parent = this;
tmp->rel_x = x;
tmp->rel_y = y;
tmp->abs_x = x + rel_x;
tmp->abs_y = y + rel_y;
tmp->data = data + y*row_scale + x;
tmp->rdata = rdata + y*row_scale + x;
tmp->wdata = wdata + y*row_scale + x;
tmp->width = w;
tmp->height = h;
tmp->row_scale = row_scale;
return tmp;
}
int GrRegion::MaxX()
{
return width-1;
}
int GrRegion::MaxY()
{
return height-1;
}
int GrRegion::SizeX()
{
return width;
}
int GrRegion::SizeY()
{
return height;
}
void GrRegion::Plot(int x, int y, int c)
{
if (!data) return;
if (c == -1) c = color;
if ((x < 0) || (y < 0) || (x >= width) || (y >= height))
return;
if (c & 0x100)
data[x+y*row_scale] ^= c;
else
data[x+y*row_scale] = c;
}
/** Test a single point to be within the xleft,xright,ybot,ytop bbox.
** Sets the returned integers 4 l.s.b. as follows:
** bit 0 if to the left of xleft.
** bit 1 if to the right of xright.
** bit 2 if above of ytop.
** bit 3 if below of ybot.
** 0 is returned if inside.
*/
static inline int clipPoint(int x, int y, int width, int height)
{
int ret_val = 0;
if (x < 0) ret_val |= 0x01;
if (x > width) ret_val |= 0x02;
if (y < 0) ret_val |= 0x04;
if (y > height) ret_val |= 0x08;
return ret_val;
}
/**
** Draw a line assumed to be clipped with respect to current region.
**/
static void drawClippedLine(GrRegion *Region,
int x1, int y1, int x2, int y2, int c)
{
unsigned char *d = &Region->data[x1+y1*Region->row_scale];
if (c == -1) c = Region->color;
if (x1 == x2)
{
Region->VLine(x1, y1, y2, c);
return;
}
if (y1 == y2)
{
Region->HLine(x1, x2, y1, c);
return;
}
int dx, dy, sx, sy;
int count;
int brc, brmax;
sx = 1;
sy = Region->row_scale;
dx = x2 - x1;
dy = y2 - y1;
if (dx < 0)
{
dx = -dx;
sx = -sx;
}
if (dy < 0)
{
dy = -dy;
sy = -sy;
}
if (c & 0x100)
*d ^= c;
else
*d = c;
if (dx > dy)
{
brmax = dx;
brc = dx / 2;
for (count = dx; count; count--)
{
d += sx;
brc += dy;
if (brc > brmax)
{
brc -= dx;
d += sy;
}
if (c & 0x100)
*d ^= c;
else
*d = c;
}
}
else
{
brmax = dy;
brc = dy / 2;
for (count = dy; count; count--)
{
d += sy;
brc += dx;
if (brc > brmax)
{
brc -= dy;
d += sx;
}
if (c & 0x100)
*d ^= c;
else
*d = c;
}
}
}
static inline int min(int x1, int x2)
{
return x1 < x2 ? x1 : x2;
}
static inline int max(int x1, int x2)
{
return x1 > x2 ? x1 : x2;
}
/** Clip and draw the given line to viewport defined using clipX/Y/min/max.
** This routine uses the cohen & sutherland bit mapping for fast clipping -
** see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
*/
static void clipLine(GrRegion *Region, int x1, int y1, int x2, int y2, int c)
{
int x, y, x_intr[4], y_intr[4], count, pos1, pos2,
width = Region->width - 1,
height = Region->height - 1;
long dx, dy;
pos1 = clipPoint(x1, y1, width, height);
pos2 = clipPoint(x2, y2, width, height);
if (pos1 || pos2) {
int x_max, x_min, y_max, y_min;
if (pos1 & pos2) return; /* segment is totally out. */
x_min = min(x1, x2);
x_max = max(x1, x2);
y_min = min(y1, y2);
y_max = max(y1, y2);
/* Compute a bound on extrema of clipped line. */
x_min = max(x_min, 0);
x_max = min(x_max, width);
y_min = max(y_min, 0);
y_max = min(y_max, height);
/* Here part of the segment MAY be inside. test the intersection
* of this segment with the 4 boundaries for hopefully 2 intersections
* in. If non found segment is totaly out.
*/
count = 0;
dx = (long) (x2 - x1);
dy = (long) (y2 - y1);
/* Find intersections with the x parallel bbox lines: */
if (dy != 0) {
if (0 == y_min) { /* Test clipYmin boundary. */
x = (int) (-y2 * dx / dy + x2);
if (x >= x_min && x <= x_max) {
x_intr[count] = x;
y_intr[count++] = 0;
}
}
if (height == y_max) { /* Test clipYmax boundary. */
x = (int) ((height - y2) * dx / dy + x2);
if (x >= x_min && x <= x_max) {
x_intr[count] = x;
y_intr[count++] = height;
}
}
}
/* Find intersections with the y parallel bbox lines: */
if (dx != 0) {
if (0 == x_min) { /* Test clipXmin boundary. */
y = (int) (-x2 * dy / dx + y2);
if (y >= y_min && y <= y_max) {
x_intr[count] = 0;
y_intr[count++] = y;
}
}
if (width == x_max) { /* Test clipXmax boundary. */
y = (int) ((width - x2) * dy / dx + y2);
if (y >= y_min && y <= y_max) {
x_intr[count] = width;
y_intr[count++] = y;
}
}
}
if (count >= 2) {
if (pos1 && pos2) { /* Both were out - update both. */
x1 = x_intr[0];
y1 = y_intr[0];
x2 = x_intr[1];
y2 = y_intr[1];
}
else if (pos1) { /* Only x1/y1 was out - update only it. */
if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
x1 = x_intr[0];
y1 = y_intr[0];
}
else {
x1 = x_intr[1];
y1 = y_intr[1];
}
}
else { /* Only x2/y2 was out - update only it. */
if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - x1) > 0) {
x2 = x_intr[0];
y2 = y_intr[0];
}
else {
x2 = x_intr[1];
y2 = y_intr[1];
}
}
}
else if (count == 1) {
if (pos1) { /* Only x1/y1 was out - update only it. */
x1 = x_intr[0];
y1 = y_intr[0];
}
else { /* Only x2/y2 was out - update only it. */
x2 = x_intr[0];
y2 = y_intr[0];
}
}
else { /* Count = 0 which means the lines is totally out. */
return;
}
if (x1 < x_min || x1 > x_max ||
x2 < x_min || x2 > x_max ||
y1 < y_min || y1 > y_max ||
y2 < y_min || y2 > y_max) {
/* This is extreme and rare case in which integer round off causes */
/* no trimming in one direction. */
/* Eliminate the trivial cases in which the line end point is on */
/* the viewport boundary and the line is totally out. */
if ((x1 <= x_min && x2 <= x_min) ||
(x1 >= x_max && x2 >= x_max) ||
(y1 <= y_min && y2 <= y_min) ||
(y1 >= y_max && y2 >= y_max))
return;
/* Otherwise try to clip again... */
clipLine(Region, x1, y1, x2, y2, c);
return;
}
}
drawClippedLine(Region, x1, y1, x2, y2, c);
}
void GrRegion::Line(int x1, int y1, int x2, int y2, int c)
{
if (!data) return;
if (c == -1) c = color;
if (x1 == x2)
VLine(x1, y1, y2, c);
else if (y1 == y2)
HLine(x1, x2, y1, c);
else
clipLine(this, x1, y1, x2, y2, c);
}
void GrRegion::HLine(int x1, int x2, int y, int c)
{
if (!data) return;
if (c == -1) c = color;
if ((y < 0) || (y >= height))
return;
if (x1 > x2)
{
x1 ^= x2; x2 ^= x1; x1 ^= x2;
}
if ((x1 >= width) || (x2 < 0))
return;
if (x1 < 0) x1 = 0;
if (x2 >= width) x2 = width - 1;
if (c & 0x100)
{
register int cnt=x2-x1+1;
register unsigned char *ptr = data+x1+y*row_scale;
while(cnt--)
*ptr++ ^= c;
}
else
memset(data + x1 + y * row_scale, c, x2 - x1 + 1);
}
void GrRegion::VLine(int x, int y1, int y2, int c)
{
if (!data) return;
if (c == -1) c = color;
if ((x < 0) || (x >= width))
return;
if (y1 > y2)
{
y1 ^= y2; y2 ^= y1; y1 ^= y2;
}
if ((y1 >= height) || (y2 < 0))
return;
if (y1 < 0) y1 = 0;
if (y2 >= height) y2 = height-1;
register unsigned char *ptr = data + y1 * row_scale + x;
register int yc=y2-y1+1;
register int rs = row_scale;
if (c & 0x100)
{
for (; yc; yc--, ptr+=rs)
*ptr ^= c;
}
else
{
for (; yc; yc--, ptr+=rs)
*ptr = c;
}
}
void GrRegion::Rectangle(int x1, int y1, int x2, int y2, int c)
{
if (!data) return;
if (c == -1) c = color;
if (x1 > x2)
{
x1 ^= x2; x2 ^= x1; x1 ^= x2;
}
if (y1 > y2)
{
y1 ^= y2; y2 ^= y1; y1 ^= y2;
}
if ((x1 == x2) || (y1 == y2))
{
Line(x1, y1, x2, y2, c);
return;
}
Line(x1, y1, x2, y1, c); // top
Line(x1, y1+1, x1, y2, c); // left
Line(x2, y1+1, x2, y2, c); // right
Line(x1+1, y2, x2-1, y2, c); // botton
}
void GrRegion::Box(int x, int y, int w, int h, int c)
{
if (!data) return;
if (c == -1) c = color;
if (x < 0)
{
w += x;
x = 0;
}
if (y < 0)
{
h += y;
y = 0;
}
if (x > width)
return;
if (y > height)
return;
if (w > width)
w = width;
if (h > height)
h = height;
if ((w <= 0) || (h <= 0))
return;
if (c & 0x100)
{
while (h--)
HLine(x, x+w-1, y++, c);
}
else
{
while (h--)
memset(data+x+(y++)*row_scale, c, w);
}
}
static unsigned char *fontptr=0;
extern "C" void int10(REGISTERS *);
void GrRegion::Text(int x, int y, char *text, int fgc, int bgc)
{
if (!data) return;
if (fgc == -1) fgc = color;
if (!fontptr)
{
REGISTERS r;
r.ax = 0x1130;
#define CHARHEIGHT 16 /* must correspond to value below */
r.bx = 0x0600;
int10(&r);
fontptr = (unsigned char *)r.bp;
}
int r, c, bits;
unsigned char *fp;
while (*text)
{
fp = fontptr + CHARHEIGHT * *(unsigned char *)text;
for (r=0; r<CHARHEIGHT; r++)
{
bits = *fp++;
for (c=0; c<8; c++)
if (bits & (0x80>>c))
Plot(x+c, y+r, fgc);
else if (bgc != -1)
Plot(x+c, y+r, bgc);
}
text++;
x += 8;
}
}
int GrRegion::Point(int x, int y)
{
if (!data) return 0;
if ((x < 0) || (y < 0) || (x >= width) || (y >= height))
return 0;
return data[x+y*row_scale];
}